package com.saris.primego.service.impl;

import com.fasterxml.jackson.databind.util.BeanUtil;
import com.saris.primego.common.constant.MessageConstant;
import com.saris.primego.common.constant.StatusConstant;
import com.saris.primego.common.context.BaseContext;
import com.saris.primego.common.exception.DeletionNotAllowedException;
import com.saris.primego.common.exception.EntityNotFoundException;
import com.saris.primego.common.exception.SetmealEnableFailedException;
import com.saris.primego.common.result.PageResult;
import com.saris.primego.dao.CategoryRepository;
import com.saris.primego.dao.DishRepository;
import com.saris.primego.dao.SetmealDishRepository;
import com.saris.primego.dao.SetmealRepository;
import com.saris.primego.pojo.dto.SetmealDTO;
import com.saris.primego.pojo.dto.SetmealPageQueryDTO;
import com.saris.primego.pojo.entity.Category;
import com.saris.primego.pojo.entity.Setmeal;
import com.saris.primego.pojo.entity.SetmealDish;
import com.saris.primego.pojo.vo.DishItemVO;
import com.saris.primego.pojo.vo.SetmealVO;
import com.saris.primego.service.SetmealService;
import jakarta.persistence.criteria.Predicate;
import jakarta.transaction.Transactional;
import org.modelmapper.ModelMapper;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Optional;

@Service
public class SetmealServiceImpl implements SetmealService {
    private final SetmealRepository setmealRepository;
    private final SetmealDishRepository setmealDishRepository;
    private final DishRepository dishRepository;
    private final ModelMapper modelMapper;
    private final CategoryRepository categoryRepository;

    public SetmealServiceImpl(SetmealRepository setmealRepository, SetmealDishRepository setmealDishRepository, DishRepository dishRepository, ModelMapper modelMapper, CategoryRepository categoryRepository) {
        this.setmealRepository = setmealRepository;
        this.setmealDishRepository = setmealDishRepository;
        this.dishRepository = dishRepository;
        this.modelMapper = modelMapper;
        this.categoryRepository = categoryRepository;
    }


    /**
     * 新增套餐，同时需要保存套餐和菜品的关联关系
     */
    @Transactional
    public Long saveWithDish(SetmealDTO setmealDTO) {
        Setmeal setmeal = modelMapper.map(setmealDTO, Setmeal.class);

        setmeal.setCreateTime(LocalDateTime.now());
        setmeal.setUpdateTime(LocalDateTime.now());
        setmeal.setCreateUser(BaseContext.getCurrentId());
        setmeal.setUpdateUser(BaseContext.getCurrentId());

        Long id = setmealRepository.saveAndFlush(setmeal).getId();

        List<SetmealDish> setmealDishes = setmealDTO.getSetmealDishes();
        setmealDishes.forEach(setmealDish -> {
            setmealDish.setSetmealId(id);
        });
        setmealDishRepository.saveAll(setmealDishes);
        return id;
    }


    /**
     * 分页查询
     */
    public PageResult<SetmealVO> pageQuery(SetmealPageQueryDTO setmealPageQueryDTO) {

        int size = setmealPageQueryDTO.getPageSize();
        int page = setmealPageQueryDTO.getPage();
        String name = setmealPageQueryDTO.getName();
        Long categoryId = setmealPageQueryDTO.getCategoryId();
        Integer status = setmealPageQueryDTO.getStatus();

        Pageable pageable = PageRequest.of(page - 1, size); // 由于pageNumber 从 0 开始，传入的页码需要-1

        // 动态构建查询条件
        Specification<Setmeal> spec = getSetmealSpecification(name, categoryId, status);

        Page<Setmeal> all = setmealRepository.findAll(spec, pageable);

        long totalElements = all.getTotalElements();
        List<Setmeal> content = all.getContent();

        List<SetmealVO> voList = new ArrayList<>();
        for (Setmeal setmeal : content) {
            Category category = categoryRepository.findById(setmeal.getCategoryId()).orElseThrow(()->new EntityNotFoundException(MessageConstant.CATEGORY_NOT_FOUND));
            SetmealVO setmealVO = modelMapper.map(setmeal, SetmealVO.class);
            setmealVO.setCategoryName(category.getName());
            voList.add(setmealVO);
        }


        return new PageResult<>(totalElements, voList);
    }


    /**
     * 批量删除套餐
     */
    @Transactional
    public void deleteBatch(List<Long> ids) {
        //起售中的套餐不能删除
        ids.forEach(id -> setmealRepository.findById(id).ifPresent(setmeal -> {
            if (Objects.equals(setmeal.getStatus(), StatusConstant.ENABLE))
                throw new DeletionNotAllowedException(MessageConstant.SETMEAL_ON_SALE);
        }));

        //
        ids.forEach(id -> {
            //删除套餐表中的数据
            setmealRepository.deleteById(id);
            //删除套餐菜品关系表中的数据
            setmealDishRepository.deleteAllBySetmealId(id);
        });
    }


    /**
     * 根据id查询套餐和套餐菜品关系
     */
    public SetmealVO getByIdWithDish(Long id) {
        List<SetmealDish> setmealDishes = setmealDishRepository.findAllBySetmealId(id);

        Optional<Setmeal> optionalSetmeal = setmealRepository.findById(id);
        if (optionalSetmeal.isPresent()) {
            Setmeal setmeal = optionalSetmeal.get();
            SetmealVO setmealVO = modelMapper.map(setmeal, SetmealVO.class);
            setmealVO.setSetmealDishes(setmealDishes);
            return setmealVO;
        }
        return null;
    }


    /**
     * 修改套餐
     */
    @Transactional
    public void update(SetmealDTO setmealDTO) {
        Setmeal setmeal = modelMapper.map(setmealDTO, Setmeal.class);
        setmeal.setUpdateTime(LocalDateTime.now());
        setmeal.setUpdateUser(BaseContext.getCurrentId());

        //1、修改套餐表，执行update
        setmealRepository.save(setmeal);

        //套餐id
        Long setmealId = setmealDTO.getId();

        //2、删除套餐和菜品的关联关系，操作setmeal_dish表，执行delete
        setmealDishRepository.deleteAllBySetmealId(setmealId);

        List<SetmealDish> setmealDishes = setmealDTO.getSetmealDishes();
        setmealDishes.forEach(setmealDish -> {
            setmealDish.setSetmealId(setmealId);
        });

        //3、重新插入套餐和菜品的关联关系，操作setmeal_dish表，执行insert
        setmealDishRepository.saveAll(setmealDishes);
    }

    /**
     * 套餐起售、停售
     */
    public void startOrStop(Integer status, Long id) {
        //起售套餐时，判断套餐内是否有停售菜品，有停售菜品提示"套餐内包含未启售菜品，无法启售"
        if (Objects.equals(status, StatusConstant.DISABLE)) {
            setmealDishRepository.getAllBySetmealId(id).forEach(setmeal -> {
                Long dishId = setmeal.getDishId();
                dishRepository.findById(dishId).ifPresent(dish -> {
                    if (Objects.equals(dish.getStatus(), StatusConstant.DISABLE))
                        throw new SetmealEnableFailedException(MessageConstant.SETMEAL_ENABLE_FAILED);
                });

            });
        }
        Optional<Setmeal> setmealOptional = setmealRepository.findById(id);

        setmealOptional.ifPresent(setmeal -> {
            if (Objects.equals(setmeal.getStatus(), StatusConstant.ENABLE)) {
                setmeal.setStatus(StatusConstant.DISABLE);
            } else {
                setmeal.setStatus(StatusConstant.ENABLE);
            }

            setmeal.setUpdateTime(LocalDateTime.now());
            setmeal.setUpdateUser(BaseContext.getCurrentId());

            setmealRepository.save(setmeal);
        });
    }


    /**
     * 条件查询
     */
    public List<Setmeal> list(Setmeal setmeal) {
        String name = setmeal.getName();
        Long categoryId = setmeal.getCategoryId();
        Integer status = setmeal.getStatus();

        Specification<Setmeal> spec = getSetmealSpecification(name, categoryId, status);

        return setmealRepository.findAll(spec);
    }

    private static Specification<Setmeal> getSetmealSpecification(String name, Long categoryId, Integer status) {
        return (root, query, cb) -> {
            List<Predicate> predicates = new ArrayList<>();

            // 当 name 不为空时，添加 name 条件
            if (name != null && !name.trim().isEmpty()) {
                predicates.add(cb.like(root.get("name"), "%" + name + "%"));
            }
            // 当 categoryId 不为空时，添加 type 条件
            if (categoryId != null) {
                predicates.add(cb.equal(root.get("categoryId"), categoryId));
            }

            // 当 status 不为空时，添加 type 条件
            if (status != null) {
                predicates.add(cb.equal(root.get("status"), status));
            }
            if (predicates.isEmpty()) {
                return cb.conjunction();

            } else {
                return cb.and(predicates.toArray(new Predicate[0]));
            }

        };
    }


    /**
     * 根据id查询菜品选项
     */
    @Override
    public List<DishItemVO> getDishItemById(Long id) {

        List<SetmealDish> setmealDishes = setmealDishRepository.getBySetmealId(id);
        List<DishItemVO> list = new ArrayList<>();
        if (setmealDishes != null) {

            for (SetmealDish setmealDish : setmealDishes) {
                DishItemVO vo = new DishItemVO();
                dishRepository.findById(setmealDish.getDishId()).ifPresent(dish -> {
                    vo.setName(dish.getName());
                    vo.setDescription(dish.getDescription());
                    vo.setImage(dish.getImage());
                });
                vo.setCopies(setmealDish.getCopies());
                list.add(vo);
            }

        }
        return list;
    }

    /**
     * 根据id查询菜品选项
     */
    @Override
    public List<Setmeal> list(Long categoryId) {
        return setmealRepository.findAllByCategoryIdAndStatus(categoryId, StatusConstant.ENABLE);
    }
}
